package com.hollycrm.hollysqm.core.waveform.util;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.hollycrm.hollysqm.core.voice.bean.ParseResult;
import com.hollycrm.hollysqm.core.voice.bean.WordBean;
import com.hollycrm.hollysqm.core.voice.parsing.IVoiceParse;
import com.hollycrm.hollysqm.core.voice.parsing.factory.VoiceParserFactory;
import com.hollycrm.hollysqm.core.waveform.plot.Plot;
import com.hollycrm.hollysqm.core.waveform.waveaccess.WaveFileReader;
import com.hollycrm.hollysqm.core.waveform.wordcloud.WordPlot;
import com.hollycrm.hollysqm.core.waveform.wordcloud.WordsLine;

import it.sauronsoftware.jave.AudioAttributes;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.EncodingAttributes;

/**
 * Created by zhaihw on 2016/5/27.
 * 音频工具类
 */
public class AudioUtil {
	
	private final static Logger log =Logger.getLogger(AudioUtil.class);
	
    //v3
    public static int v3freq = 6000;

    private AudioUtil() {
    }

    /**
     * 将v3的音频文件转换为wav
     *
     * @param v3file   v3file
     * @param waveFile wav存储路径
     */
    public static boolean covertV3ToWav(String v3file, String waveFile) throws IOException {
        if (StringUtils.isEmpty(v3file) || StringUtils.isEmpty(waveFile))
            return false;
        File v3File = new File(v3file);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(v3File));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(waveFile)));
        int[] indsft = {-1, -1, -1, -1, 2, 4, 6, 8},
                stpsz = {16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55,
                        60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209,
                        230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
                        724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878,
                        2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871,
                        5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635,
                        13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794},
                sgns = {1, -1};

        /* nibble to bit map */
        int nbl2bit[][] = {{0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0},
                {0, 0, 1, 1}, {0, 1, 0, 0}, {0, 1, 0, 1}, {0, 1, 1, 0},
                {0, 1, 1, 1}, {1, 0, 0, 0}, {1, 0, 0, 1}, {1, 0, 1, 0},
                {1, 0, 1, 1}, {1, 1, 0, 0}, {1, 1, 0, 1}, {1, 1, 1, 0},
                {1, 1, 1, 1}};
        /* step size index */

        long lPCMHDR[] = {0x46464952, 0, 0x45564157, 0x20746d66, 16,
                0x10001, 0, 0, 0x100002, 0x61746164, 0};
        lPCMHDR[1] = 4 * v3File.length() + 0x30; // 文件长度
        lPCMHDR[6] = v3freq;
        lPCMHDR[7] = 2 * v3freq;
        lPCMHDR[10] = 4 * v3File.length();

        byte[] b = new byte[44];
        long2Byte(b, lPCMHDR, 44);
        bos.write(b, 0, b.length);

        short iVal = 0, incoded;
        int pos = 0, ssindex = 0, diff, dwRead;
        byte[] szBuf = new byte[128 * 1024],szDesBuf = new byte[4 * 128 * 1024];
        while ((dwRead = bis.read(szBuf, 0, 128 * 1024)) != -1) {
            for (int idx = 0; idx < dwRead - 1; idx++) {
                incoded = (short) (byte2Short(szBuf[idx]) / 16);
                diff = sgns[nbl2bit[incoded][0]]
                        * (stpsz[ssindex] * nbl2bit[incoded][1]
                        + (stpsz[ssindex] / 2)
                        * nbl2bit[incoded][2]
                        + (stpsz[ssindex] / 4)
                        * nbl2bit[incoded][3] + (stpsz[ssindex] / 8));

                ssindex = ssindex + indsft[(incoded % 8)];

                if (ssindex < 0)
                    ssindex = 0;
                if (ssindex > 48)
                    ssindex = 48;
                iVal += diff;

                if (iVal > 2047)
                    iVal = 2047;
                else if (iVal < -2047)
                    iVal = -2047;

                byte[] b2 = new byte[2];
                short2Byte(b2, (short) (iVal * 16));

                szDesBuf[pos] = b2[0];
                pos++;
                szDesBuf[pos] = b2[1];
                pos++;

                incoded = (short) (byte2Short(szBuf[idx]) % 16);

                diff = sgns[nbl2bit[incoded][0]]
                        * (stpsz[ssindex] * nbl2bit[incoded][1]
                        + (stpsz[ssindex] / 2)
                        * nbl2bit[incoded][2]
                        + (stpsz[ssindex] / 4)
                        * nbl2bit[incoded][3] + (stpsz[ssindex] / 8));

                ssindex = ssindex + indsft[(incoded % 8)];

                if (ssindex < 0)
                    ssindex = 0;
                if (ssindex > 48)
                    ssindex = 48;
                iVal += diff;
                //波长
                if (iVal > 2047)
                    iVal = 2047;
                else if (iVal < -2047)
                    iVal = -2047;

                byte[] b3 = new byte[2];
                short2Byte(b3, (short) (iVal * 16));
                szDesBuf[pos] = b3[0];
                pos++;
                szDesBuf[pos] = b3[1];
                pos++;
            }
            bos.write(szDesBuf, 0, dwRead * 4);
            bos.flush();
            pos = 0;
        }
        try {
            bos.close();
            bis.close();
        } catch (Exception e) {
        }
        return true;
    }

    public static void long2Byte(byte[] output, long[] input, int len) {
        int i, j;
        for (i = 0, j = 0; j < len; i++, j += 4) {
            output[j] = (byte) (input[i] & 0xffL);
            output[j + 1] = (byte) ((input[i] >>> 8) & 0xffL);
            output[j + 2] = (byte) ((input[i] >>> 16) & 0xffL);
            output[j + 3] = (byte) ((input[i] >>> 24) & 0xffL);
        }
    }
    public static short byte2Short(byte b) {
        return b < 0 ? (short) (b & 0x7F + 128) : b;
    }
    public static void short2Byte(byte[] output, short sh) {
        output[0] = (byte) (sh & 0xffL);
        output[1] = (byte) ((sh >>> 8) & 0xffL);
    }
    public static double[] int2Doubles(int[] raw) {
        double[] res = new double[raw.length];
        for (int i = 0; i < res.length; i++) {
            res[i] = raw[i];
        }
        return res;
    }
    
    /**
     * 将Mp3文件转换为wav
     * @param wavFile wav file
     * @param mp3File mp3 file
     * @throws Exception
     */
    public static boolean covertMp3ToWav(String mp3File, String wavFile) throws Exception{
        File source = new File(mp3File);
        File target = new File(wavFile);
        AudioAttributes audio = new AudioAttributes();
        //pcm_s16le libmp3lame libvorbis libfaac 
        audio.setCodec("pcm_s16le");
        audio.setBitRate(640);
        audio.setChannels(1);
        audio.setSamplingRate(22050);
        EncodingAttributes attrs = new EncodingAttributes();
        attrs.setFormat("wav");
        attrs.setAudioAttributes(audio);
        Encoder encoder = new Encoder();
        encoder.encode(source, target, attrs);
        return true;
    }
    
    /**
     * 将wav文件转换为Mp3
     * @param wavFile wav file
     * @param mp3File mp3 file
     * @throws Exception
     */
    public static boolean covertWav2Mp3(String wavFile, String mp3File) throws Exception{
        File source = new File(wavFile);
        File target = new File(mp3File);
        AudioAttributes audio = new AudioAttributes();
        audio.setCodec("libmp3lame");
        audio.setBitRate(640);
        audio.setChannels(1);
        audio.setSamplingRate(22050);
        EncodingAttributes attrs = new EncodingAttributes();
        attrs.setFormat("mp3");
        attrs.setAudioAttributes(audio);
        Encoder encoder = new Encoder();
        encoder.encode(source, target, attrs);
        return true;
    }

    /**
     * 绘制波形图
     * @param wavFile wav文件路径
     * @param img 保存图片路径
     * @param width 图片宽度
     * @param height 图片高度
     * @return 是否成功
     */
    public static boolean drawWavImage(String wavFile, String img, int width, int height) {
        WaveFileReader reader = new WaveFileReader(wavFile);
        if (!reader.isSuccess())
            return false;
        Plot plot = new Plot();
        String[] param = new String[]{"-c"}; //* o n $ z -
        plot.hold_on();
        for (int i = 0; i < reader.getNumChannels(); ++i) {
            int[] data = reader.getData()[i];
            plot.plot(int2Doubles(data), param[i % param.length], img, width, height, Color.WHITE);
        }
        plot.hold_off();
        return true;
    }

    /**
     * 重绘波形图，增加文字信息
     * @param img 图片路径
     * @param words 文字内容
     * @return 是否成功
     */
    public static boolean repaintWavImage (String img, List<WordBean> words, Set<String> userWords,double ratio) throws IOException{
        List<WordPlot> wps = new ArrayList<>();
        if(words == null || words.size() == 0)
            return false;
        for(WordBean bean : words) {
            int y = 20, x, w, h = 20;
            if(bean.getRole() == 1)
                y = 160;
            w = 16 * bean.getText().length();
            x = (int) Math.ceil ((bean.getStartTime() / 10) / ratio);
            x = x - w < 0 ? x : x - w;
            WordPlot plot = new WordPlot(bean.getText(), x, y, w, h);
            if( userWords != null && userWords.contains(bean.getText())) //关键词标记
                plot.setLight(true);
            wps.add(plot);
        }
        BufferedImage bi = WordsLine.createWordsLine(img, wps, "仿宋", 16);
        ImageIO.write(bi, "jpg", new File(img));
        return true;
    }
    
    /**
     * 创建需要绘制成图片的Wav音频格式以及将XML文本写入到图片中
     * @param mp3File
     * @param wavFile
     * @param xmlFile
     * @param imgFile
     * @return
     * @throws Exception
     */
    public static boolean createWavAndImage(String mp3File,String wavFile,String xmlFile,String imgFile) throws Exception{    
    	log.info("Mp3文件转换为wav格式, wavFile = " + wavFile);
    	covertMp3ToWav(mp3File,wavFile);
    	log.info("xml文件解析，xmlFile = " + xmlFile);
        //xml解析
        IVoiceParse parse = VoiceParserFactory.getInstance().defaultParser(new FileInputStream(new File(xmlFile)), true);
        ParseResult pr = parse.parse();
        //图形绘制
        int imgW = (int) pr.getDuration() / 10;
        double k = 1D, ration = 1D;
        while (imgW > 60000) {
            ration = k * (k + 0.1);
            k = k + 0.1;
            imgW = (int) Math.ceil(imgW / k);
        }
        log.info("绘制波形图，imgWidth = " + imgW + ", imgHeight = 220");
        AudioUtil.drawWavImage(wavFile, imgFile, imgW, 200); //绘制波形图
        log.info("重绘制波形图，加入文字对话信息");
        AudioUtil.repaintWavImage(imgFile, pr.getWordTimes(), null, ration); // 重绘，添加文字信息
        //删除无用文件
        log.info("删除临时文件wav, xml");
        FileUtils.deleteQuietly(new File(wavFile));
        FileUtils.deleteQuietly(new File(xmlFile));
        log.info("波形图创建结束");    	
    	return true;
    }
    
    public static void main(String[] args) {
//        String v3file = "C:\\Users\\Mr\\Desktop\\bin\\00D6B6E9B80E448A82F2AB979ED8FE4E.V3";
        String wavFile = "C:\\Users\\Mr\\Desktop\\bin\\00D6B6E9B80E448A82F2AB979ED8FE4E.wav";
//        String mp3File = "C:\\Users\\Mr\\Desktop\\bin\\00D6B6E9B80E448A82F2AB979ED8FE4E.mp3";
        String imgFile = "C:\\Users\\Mr\\Desktop\\bin\\00D6B6E9B80E448A82F2AB979ED8FE4E.jpg";
        String xmlFile = "C:\\Users\\Mr\\Desktop\\bin\\00D6B6E9B80E448A82F2AB979ED8FE4E.xml";
        try {
            long l = System.currentTimeMillis();
            IVoiceParse parse = VoiceParserFactory.getInstance().defaultParser(new FileInputStream(new File(xmlFile)),true);
            ParseResult pr = parse.parse();
            List<WordBean> wbs = pr.getWordTimes();
            int imgW = (int) pr.getDuration() / 10;
            double k =1D, ration = 1D;
            while(imgW > 60000) {
                ration = k * (k + 0.1);
                k = k + 0.1;
                imgW = (int) Math.ceil(imgW / k);
            }
            drawWavImage(wavFile, imgFile, imgW, 220);
            Set<String> userWords = new HashSet<>();
            userWords.add("宽带");
            userWords.add("查一下");
            userWords.add("账号");
            userWords.add("核实");
            userWords.add("上网");
            repaintWavImage(imgFile, wbs, userWords,ration);
            System.out.println(System.currentTimeMillis() - l);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
